/*-------------------------------------------------------------------------
 *
 * parse_func.c
 *		在解析器中处理函数调用
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/parser/parse_func.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "lib/stringinfo.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"


/* LookupFuncNameInternal可能的错误代码 */
typedef enum
{
	FUNCLOOKUP_NOSUCHFUNC,
	FUNCLOOKUP_AMBIGUOUS
} FuncLookupError;

static void fc_unify_hypothetical_args(ParseState *fc_pstate,
									List *fc_fargs, int fc_numAggregatedArgs,
									Oid *fc_actual_arg_types, Oid *fc_declared_arg_types);
static Oid	fc_FuncNameAsType(List *fc_funcname);
static Node *fc_ParseComplexProjection(ParseState *fc_pstate, const char *fc_funcname,
									Node *fc_first_arg, int fc_location);
static Oid	fc_LookupFuncNameInternal(ObjectType fc_objtype, List *fc_funcname,
								   int fc_nargs, const Oid *fc_argtypes,
								   bool fc_include_out_arguments, bool fc_missing_ok,
								   FuncLookupError *fc_lookupError);


/*
 *	解析函数调用
 *
 *	由于历史原因，Postgres尝试将表示法tab.col
 *	和col(tab)视为等效：如果单参数函数调用的
 *	参数是复杂类型，并且（未限定的）函数名与
 *	该类型的任何属性匹配，我们可以将其解释为
 *	列投影。相反，单复杂类型参数的函数可以
 *	写成列引用的形式，这允许函数像计算列一样
 *	工作。
 *
 *	如果两种解释都是可能的，我们更倾向于匹配
 *	语法形式的解释，但否则形式并不重要。
 *
 *	因此，这两种情况都会经过这里。如果fn为null，
 *	我们正在处理列语法，而不是函数语法。在
 *	函数语法的情况下，需要FuncCall结构来承载
 *	适用于聚合和窗口函数的各种装饰。
 *
 *	此外，当fn为null时，我们在失败时返回NULL，而
 *	不是报告没有这样的函数的错误。
 *
 *	参数表达式（在fargs中）必须已经被转换。
 *	然而，*fn中的内容尚未被转换。
 *
 *	last_srf应该是pstate->p_last_srf的副本，
 *	来自我们开始转换fargs之前。如果调用者
 *	知道fargs中不可能包含任何SRF调用，
 *	last_srf可以直接是pstate->p_last_srf。
 *
 *	proc_call为真时，表示我们正在考虑CALL语句，
 *	因此名称必须解析为过程名称，而不是其他任何内容。
 *	此标志还指定参数列表中包含任何OUT模式参数。
 */
Node * ParseFuncOrColumn(ParseState *fc_pstate, List *fc_funcname, List *fc_fargs,
				  Node *fc_last_srf, FuncCall *fc_fn, bool fc_proc_call, int fc_location)
{
	bool		fc_is_column = (fc_fn == NULL);
	List	   *fc_agg_order = (fc_fn ? fc_fn->agg_order : NIL);
	Expr	   *fc_agg_filter = NULL;
	WindowDef  *fc_over = (fc_fn ? fc_fn->over : NULL);
	bool		fc_agg_within_group = (fc_fn ? fc_fn->agg_within_group : false);
	bool		fc_agg_star = (fc_fn ? fc_fn->agg_star : false);
	bool		fc_agg_distinct = (fc_fn ? fc_fn->agg_distinct : false);
	bool		fc_func_variadic = (fc_fn ? fc_fn->func_variadic : false);
	CoercionForm fc_funcformat = (fc_fn ? fc_fn->funcformat : COERCE_EXPLICIT_CALL);
	bool		fc_could_be_projection;
	Oid			fc_rettype;
	Oid			fc_funcid;
	ListCell   *fc_l;
	Node	   *fc_first_arg = NULL;
	int			fc_nargs;
	int			fc_nargsplusdefs;
	Oid			fc_actual_arg_types[FUNC_MAX_ARGS];
	Oid		   *fc_declared_arg_types;
	List	   *fc_argnames;
	List	   *fc_argdefaults;
	Node	   *fc_retval;
	bool		fc_retset;
	int			fc_nvargs;
	Oid			fc_vatype;
	FuncDetailCode fc_fdresult;
	char		fc_aggkind = 0;
	ParseCallbackState fc_pcbstate;

	/*
	 * 如果有聚合过滤器，使用transformWhereClause转换它
	 */
	if (fc_fn && fc_fn->agg_filter != NULL)
		fc_agg_filter = (Expr *) transformWhereClause(fc_pstate, fc_fn->agg_filter,
												   EXPR_KIND_FILTER,
												   "FILTER");

	/*
	 * 解析器的其余大部分只是假设函数没有超过
	 * FUNC_MAX_ARGS参数。我们在这里必须测试以防止
	 * 数组溢出等。当然，这可能不是一个函数，
	 * 但是测试并没有坏处。
	 */
	if (list_length(fc_fargs) > FUNC_MAX_ARGS)
		ereport(ERROR,
				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
				 errmsg_plural("cannot pass more than %d argument to a function",
							   "cannot pass more than %d arguments to a function",
							   FUNC_MAX_ARGS,
							   FUNC_MAX_ARGS),
				 parser_errposition(fc_pstate, fc_location)));

	/*
	 * 提取参数类型信息以准备函数查找。
	 *
	 * 如果任何参数是类型为VOID的Param标记，
	 * 我们将其从参数列表中丢弃。这是一个黑客
	 * 解决方案，允许JDBC驱动在解析函数调用
	 * 构造时不必区分“输入”和“输出”参数符号。
	 * 如果处理列语法，或者我们有WITHIN GROUP的情况，
	 * 则不要这样做（因为在这种情况下保持参数数量
	 * 不变是至关重要的）。
	 */
	fc_nargs = 0;
	foreach(fc_l, fc_fargs)
	{
		Node	   *fc_arg = lfirst(fc_l);
		Oid			fc_argtype = exprType(fc_arg);

		if (fc_argtype == VOIDOID && IsA(fc_arg, Param) &&
			!fc_is_column && !fc_agg_within_group)
		{
			fc_fargs = foreach_delete_current(fc_fargs, fc_l);
			continue;
		}

		fc_actual_arg_types[fc_nargs++] = fc_argtype;
	}

	/*
	 * 检查命名参数；如果有的话，构建一个名称列表。
	 *
	 * 我们允许混合表示法（一些命名参数和一些
	 * 未命名参数），但仅在所有命名参数都在
	 * 所有未命名参数之后。因此，名称列表
	 * 对应于最后N个实际参数，我们不需要额外的
	 * 记账来匹配它们。
	 */
	fc_argnames = NIL;
	foreach(fc_l, fc_fargs)
	{
		Node	   *fc_arg = lfirst(fc_l);

		if (IsA(fc_arg, NamedArgExpr))
		{
			NamedArgExpr *fc_na = (NamedArgExpr *) fc_arg;
			ListCell   *fc_lc;

			/* 拒绝重复的参数名称 */
			foreach(fc_lc, fc_argnames)
			{
				if (strcmp(fc_na->name, (char *) lfirst(fc_lc)) == 0)
					ereport(ERROR,
							(errcode(ERRCODE_SYNTAX_ERROR),
							 errmsg("argument name \"%s\" used more than once",
									fc_na->name),
							 parser_errposition(fc_pstate, fc_na->location)));
			}
			fc_argnames = lappend(fc_argnames, fc_na->name);
		}
		else
		{
			if (fc_argnames != NIL)
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("positional argument cannot follow named argument"),
						 parser_errposition(fc_pstate, exprLocation(fc_arg))));
		}
	}

	if (fc_fargs)
	{
		fc_first_arg = linitial(fc_fargs);
		Assert(fc_first_arg != NULL);
	}

	/*
	 * 决定将构造视为列投影是否是合法的。
	 * 为此，必须有一个复杂类型的单个参数，
	 * 函数名称不得限定，并且不能有任何语法
	 * 装饰会要求其成为函数（例如聚合或可变参数装饰，
	 * 或命名参数）。
	 */
	fc_could_be_projection = (fc_nargs == 1 && !fc_proc_call &&
						   fc_agg_order == NIL && fc_agg_filter == NULL &&
						   !fc_agg_star && !fc_agg_distinct && fc_over == NULL &&
						   !fc_func_variadic && fc_argnames == NIL &&
						   list_length(fc_funcname) == 1 &&
						   (fc_actual_arg_types[0] == RECORDOID ||
							ISCOMPLEX(fc_actual_arg_types[0])));

	/*
	 * 如果是列语法，首先检查列投影情况。
	 */
	if (fc_could_be_projection && fc_is_column)
	{
		fc_retval = fc_ParseComplexProjection(fc_pstate,
										strVal(linitial(fc_funcname)),
										fc_first_arg,
										fc_location);
		if (fc_retval)
			return fc_retval;

		/*
		 * 如果ParseComplexProjection没有将其识别为投影，
		 * 那就继续。
		 */
	}

	/*
	 * func_get_detail在目录中查找函数，进行
	 * 多态函数的消歧，处理继承，并返回函数
	 * 的funcid、类型和集合或单例状态。
	 * 它还返回函数的真实参数类型。
	 *
	 * 注意：对于命名表示法或可变参数函数调用，报告的
	 * “真实”类型实际上与pg_proc中的类型并不完全相符：
	 * 类型是重新排序的，以匹配命名参数的给定参数顺序，
	 * 可变参数被其元素类型的适当数量的副本所替代。
	 * 我们将在下面修正可变参数的情况。我们也可能
	 * 必须处理默认参数。
	 */

	setup_parser_errposition_callback(&fc_pcbstate, fc_pstate, fc_location);

	fc_fdresult = func_get_detail(fc_funcname, fc_fargs, fc_argnames, fc_nargs,
							   fc_actual_arg_types,
							   !fc_func_variadic, true, fc_proc_call,
							   &fc_funcid, &fc_rettype, &fc_retset,
							   &fc_nvargs, &fc_vatype,
							   &fc_declared_arg_types, &fc_argdefaults);

	cancel_parser_errposition_callback(&fc_pcbstate);

	/*
	 * 检查各种不正确类型的例程情况。
	 */

	/* 如果这是CALL，拒绝不是过程的内容 */
	if (fc_proc_call &&
		(fc_fdresult == FUNCDETAIL_NORMAL ||
		 fc_fdresult == FUNCDETAIL_AGGREGATE ||
		 fc_fdresult == FUNCDETAIL_WINDOWFUNC ||
		 fc_fdresult == FUNCDETAIL_COERCION))
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("%s is not a procedure",
						func_signature_string(fc_funcname, fc_nargs,
											  fc_argnames,
											  fc_actual_arg_types)),
				 errhint("To call a function, use SELECT."),
				 parser_errposition(fc_pstate, fc_location)));
	/* 相反，如果不是CALL，拒绝过程 */
	if (fc_fdresult == FUNCDETAIL_PROCEDURE && !fc_proc_call)
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("%s is a procedure",
						func_signature_string(fc_funcname, fc_nargs,
											  fc_argnames,
											  fc_actual_arg_types)),
				 errhint("To call a procedure, use CALL."),
				 parser_errposition(fc_pstate, fc_location)));

	if (fc_fdresult == FUNCDETAIL_NORMAL ||
		fc_fdresult == FUNCDETAIL_PROCEDURE ||
		fc_fdresult == FUNCDETAIL_COERCION)
	{
		/*
		 * 在这些情况下，如果有任何指示它必须是
		 * 聚合或窗口函数的内容，则会抱怨。
		 */
		if (fc_agg_star)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("%s(*) specified, but %s is not an aggregate function",
							NameListToString(fc_funcname),
							NameListToString(fc_funcname)),
					 parser_errposition(fc_pstate, fc_location)));
		if (fc_agg_distinct)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("DISTINCT specified, but %s is not an aggregate function",
							NameListToString(fc_funcname)),
					 parser_errposition(fc_pstate, fc_location)));
		if (fc_agg_within_group)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("WITHIN GROUP specified, but %s is not an aggregate function",
							NameListToString(fc_funcname)),
					 parser_errposition(fc_pstate, fc_location)));
		if (fc_agg_order != NIL)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("ORDER BY specified, but %s is not an aggregate function",
							NameListToString(fc_funcname)),
					 parser_errposition(fc_pstate, fc_location)));
		if (fc_agg_filter)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("FILTER specified, but %s is not an aggregate function",
							NameListToString(fc_funcname)),
					 parser_errposition(fc_pstate, fc_location)));
		if (fc_over)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("OVER specified, but %s is not a window function nor an aggregate function",
							NameListToString(fc_funcname)),
					 parser_errposition(fc_pstate, fc_location)));
	}

	/*
	 * 到这里为止还好，所以执行一些特定于
	 * fdresult类型的处理。
	 */
	if (fc_fdresult == FUNCDETAIL_NORMAL || fc_fdresult == FUNCDETAIL_PROCEDURE)
	{
		/* 对于这些情况没有特别需要做的。 */
	}
	else if (fc_fdresult == FUNCDETAIL_AGGREGATE)
	{
		/*
		 * 这是一个聚合；从pg_aggregate条目中提取
		 * 所需信息。
		 */
		HeapTuple	fc_tup;
		Form_pg_aggregate fc_classForm;
		int			fc_catDirectArgs;

		fc_tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(fc_funcid));
		if (!HeapTupleIsValid(fc_tup)) /* 不应该发生 */
			elog(ERROR, "cache lookup failed for aggregate %u", fc_funcid);
		fc_classForm = (Form_pg_aggregate) GETSTRUCT(fc_tup);
		fc_aggkind = fc_classForm->aggkind;
		fc_catDirectArgs = fc_classForm->aggnumdirectargs;
		ReleaseSysCache(fc_tup);

		/* 现在检查各种不允许的情况。 */
		if (AGGKIND_IS_ORDERED_SET(fc_aggkind))
		{
			int			fc_numAggregatedArgs;
			int			fc_numDirectArgs;

			if (!fc_agg_within_group)
				ereport(ERROR,
						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
						 errmsg("WITHIN GROUP is required for ordered-set aggregate %s",
								NameListToString(fc_funcname)),
						 parser_errposition(fc_pstate, fc_location)));
			if (fc_over)
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("OVER is not supported for ordered-set aggregate %s",
								NameListToString(fc_funcname)),
						 parser_errposition(fc_pstate, fc_location)));
			/* gram.y拒绝DISTINCT和WITHIN GROUP */
			Assert(!fc_agg_distinct);
			/* gram.y 拒绝 VARIADIC + WITHIN GROUP */
			Assert(!fc_func_variadic);

			/*
			 * 由于 func_get_detail 在处理一个未区分的参数
			 * 列表，它可能选择了一个不真正匹配的聚合，因为它需要
			 * 不同的直接和聚合参数划分。 检查直接参数的数量
			 * 是否实际上是可以的；如果不可以，就抛出一个“未定义的函数”
			 * 错误，类似于在常规聚合调用中使用位置不当的 ORDER BY 的情况。
			 */
			fc_numAggregatedArgs = list_length(fc_agg_order);
			fc_numDirectArgs = fc_nargs - fc_numAggregatedArgs;
			Assert(fc_numDirectArgs >= 0);

			if (!OidIsValid(fc_vatype))
			{
				/* 如果聚合不是可变参数，测试很简单 */
				if (fc_numDirectArgs != fc_catDirectArgs)
					ereport(ERROR,
							(errcode(ERRCODE_UNDEFINED_FUNCTION),
							 errmsg("function %s does not exist",
									func_signature_string(fc_funcname, fc_nargs,
														  fc_argnames,
														  fc_actual_arg_types)),
							 errhint_plural("There is an ordered-set aggregate %s, but it requires %d direct argument, not %d.",
											"There is an ordered-set aggregate %s, but it requires %d direct arguments, not %d.",
											fc_catDirectArgs,
											NameListToString(fc_funcname),
											fc_catDirectArgs, fc_numDirectArgs),
							 parser_errposition(fc_pstate, fc_location)));
			}
			else
			{
				/*
				 * 如果是可变参数，我们有两种情况，取决于
				 * agg 是“... ORDER BY VARIADIC”还是“..., VARIADIC ORDER
				 * BY VARIADIC”。 如果 catDirectArgs 等于
				 * pronargs；为了节省目录查找，我们从 func_get_detail 获得的信息中逆向工程
				 * pronargs。
				 */
				int			fc_pronargs;

				fc_pronargs = fc_nargs;
				if (fc_nvargs > 1)
					fc_pronargs -= fc_nvargs - 1;
				if (fc_catDirectArgs < fc_pronargs)
				{
					/* VARIADIC 不是直接参数的一部分，所以仍然很简单 */
					if (fc_numDirectArgs != fc_catDirectArgs)
						ereport(ERROR,
								(errcode(ERRCODE_UNDEFINED_FUNCTION),
								 errmsg("function %s does not exist",
										func_signature_string(fc_funcname, fc_nargs,
															  fc_argnames,
															  fc_actual_arg_types)),
								 errhint_plural("There is an ordered-set aggregate %s, but it requires %d direct argument, not %d.",
												"There is an ordered-set aggregate %s, but it requires %d direct arguments, not %d.",
												fc_catDirectArgs,
												NameListToString(fc_funcname),
												fc_catDirectArgs, fc_numDirectArgs),
								 parser_errposition(fc_pstate, fc_location)));
				}
				else
				{
					/*
					 * 直接参数和聚合参数都被声明为可变参数。
					 * 对于标准有序集聚合，只要
					 * 直接参数不太少，就没问题。 对于一个
					 * 假设集聚合，我们假设假设参数是那些与
					 * 可变参数匹配的参数；它们的数量必须等于
					 * 聚合参数的数量。
					 */
					if (fc_aggkind == AGGKIND_HYPOTHETICAL)
					{
						if (fc_nvargs != 2 * fc_numAggregatedArgs)
							ereport(ERROR,
									(errcode(ERRCODE_UNDEFINED_FUNCTION),
									 errmsg("function %s does not exist",
											func_signature_string(fc_funcname, fc_nargs,
																  fc_argnames,
																  fc_actual_arg_types)),
									 errhint("To use the hypothetical-set aggregate %s, the number of hypothetical direct arguments (here %d) must match the number of ordering columns (here %d).",
											 NameListToString(fc_funcname),
											 fc_nvargs - fc_numAggregatedArgs, fc_numAggregatedArgs),
									 parser_errposition(fc_pstate, fc_location)));
					}
					else
					{
						if (fc_nvargs <= fc_numAggregatedArgs)
							ereport(ERROR,
									(errcode(ERRCODE_UNDEFINED_FUNCTION),
									 errmsg("function %s does not exist",
											func_signature_string(fc_funcname, fc_nargs,
																  fc_argnames,
																  fc_actual_arg_types)),
									 errhint_plural("There is an ordered-set aggregate %s, but it requires at least %d direct argument.",
													"There is an ordered-set aggregate %s, but it requires at least %d direct arguments.",
													fc_catDirectArgs,
													NameListToString(fc_funcname),
													fc_catDirectArgs),
									 parser_errposition(fc_pstate, fc_location)));
					}
				}
			}

			/* 检查假设参数的类型匹配 */
			if (fc_aggkind == AGGKIND_HYPOTHETICAL)
				fc_unify_hypothetical_args(fc_pstate, fc_fargs, fc_numAggregatedArgs,
										fc_actual_arg_types, fc_declared_arg_types);
		}
		else
		{
			/* 常规聚合，因此它不能具有 WITHIN GROUP */
			if (fc_agg_within_group)
				ereport(ERROR,
						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
						 errmsg("%s is not an ordered-set aggregate, so it cannot have WITHIN GROUP",
								NameListToString(fc_funcname)),
						 parser_errposition(fc_pstate, fc_location)));
		}
	}
	else if (fc_fdresult == FUNCDETAIL_WINDOWFUNC)
	{
		/*
		 * 真正的窗口函数必须使用窗口定义进行调用。
		 */
		if (!fc_over)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("window function %s requires an OVER clause",
							NameListToString(fc_funcname)),
					 parser_errposition(fc_pstate, fc_location)));
		/* 按照规范，WITHIN GROUP 是不允许的 */
		if (fc_agg_within_group)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("window function %s cannot have WITHIN GROUP",
							NameListToString(fc_funcname)),
					 parser_errposition(fc_pstate, fc_location)));
	}
	else if (fc_fdresult == FUNCDETAIL_COERCION)
	{
		/* 我们将其解释为类型强制。coerce_type 可以处理这些情况，所以为何要重复代码... */
		return coerce_type(fc_pstate, linitial(fc_fargs),
						   fc_actual_arg_types[0], fc_rettype, -1,
						   COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, fc_location);
	}
	else if (fc_fdresult == FUNCDETAIL_MULTIPLE)
	{
		/* 我们发现了多个可能的功能匹配。如果我们处理的是属性表示法，则返回失败，让调用者报告“没有这样的列”（我们已经确定没有）。如果处理的是函数表示法，则报告“模棱两可的函数”，无论是否还有一个同名列。 */
		if (fc_is_column)
			return NULL;

		if (fc_proc_call)
			ereport(ERROR,
					(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
					 errmsg("procedure %s is not unique",
							func_signature_string(fc_funcname, fc_nargs, fc_argnames,
												  fc_actual_arg_types)),
					 errhint("Could not choose a best candidate procedure. "
							 "You might need to add explicit type casts."),
					 parser_errposition(fc_pstate, fc_location)));
		else
			ereport(ERROR,
					(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
					 errmsg("function %s is not unique",
							func_signature_string(fc_funcname, fc_nargs, fc_argnames,
												  fc_actual_arg_types)),
					 errhint("Could not choose a best candidate function. "
							 "You might need to add explicit type casts."),
					 parser_errposition(fc_pstate, fc_location)));
	}
	else
	{
		/* 找不到作为函数的内容。如果我们处理的是属性表示法，则返回失败，让调用者报告“没有这样的列”（我们已经确定没有）。 */
		if (fc_is_column)
			return NULL;

		/* 检查列投影解释，因为我们之前没有检查。 */
		if (fc_could_be_projection)
		{
			fc_retval = fc_ParseComplexProjection(fc_pstate,
											strVal(linitial(fc_funcname)),
											fc_first_arg,
											fc_location);
			if (fc_retval)
				return fc_retval;
		}

		/* 没有函数，也没有列。由于我们处理的是函数表示法，报告“函数不存在”。 */
		if (list_length(fc_agg_order) > 1 && !fc_agg_within_group)
		{
			/* 这是 agg(x, ORDER BY y,z) ... 可能是 ORDER BY 放错位置 */
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("function %s does not exist",
							func_signature_string(fc_funcname, fc_nargs, fc_argnames,
												  fc_actual_arg_types)),
					 errhint("No aggregate function matches the given name and argument types. "
							 "Perhaps you misplaced ORDER BY; ORDER BY must appear "
							 "after all regular arguments of the aggregate."),
					 parser_errposition(fc_pstate, fc_location)));
		}
		else if (fc_proc_call)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("procedure %s does not exist",
							func_signature_string(fc_funcname, fc_nargs, fc_argnames,
												  fc_actual_arg_types)),
					 errhint("No procedure matches the given name and argument types. "
							 "You might need to add explicit type casts."),
					 parser_errposition(fc_pstate, fc_location)));
		else
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("function %s does not exist",
							func_signature_string(fc_funcname, fc_nargs, fc_argnames,
												  fc_actual_arg_types)),
					 errhint("No function matches the given name and argument types. "
							 "You might need to add explicit type casts."),
					 parser_errposition(fc_pstate, fc_location)));
	}

	/* 如果存在默认参数，我们必须在实际参数类型中包含它们的类型，以便检查通用类型一致性。然而，我们不将它们放入生成的解析节点，因为它们的实际值可能会在查询运行之前发生变化。计划者必须在计划时插入最新的值。 */
	fc_nargsplusdefs = fc_nargs;
	foreach(fc_l, fc_argdefaults)
	{
		Node	   *fc_expr = (Node *) lfirst(fc_l);

		/* 可能不应该发生... */
		if (fc_nargsplusdefs >= FUNC_MAX_ARGS)
			ereport(ERROR,
					(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
					 errmsg_plural("cannot pass more than %d argument to a function",
								   "cannot pass more than %d arguments to a function",
								   FUNC_MAX_ARGS,
								   FUNC_MAX_ARGS),
					 parser_errposition(fc_pstate, fc_location)));

		fc_actual_arg_types[fc_nargsplusdefs++] = exprType(fc_expr);
	}

	/* 强制保持多态参数和返回类型的一致性，可能调整返回类型或声明的参数类型（这些将被 make_fn_arguments 用作转换目标） */
	fc_rettype = enforce_generic_type_consistency(fc_actual_arg_types,
											   fc_declared_arg_types,
											   fc_nargsplusdefs,
											   fc_rettype,
											   false);

	/* 执行必要的参数类型转换 */
	make_fn_arguments(fc_pstate, fc_fargs, fc_actual_arg_types, fc_declared_arg_types);

	/* 如果函数实际上不是可变参数的，忘记调用中的任何可变参数装饰。（也许我们应该抛出一个错误，但从历史上看，我们允许人们这样写。） */
	if (!OidIsValid(fc_vatype))
	{
		Assert(fc_nvargs == 0);
		fc_func_variadic = false;
	}

	/* 如果这是一个可变参数函数调用，将最后 nvargs 个参数转换为数组——除非它是“任何”可变参数。 */
	if (fc_nvargs > 0 && fc_vatype != ANYOID)
	{
		ArrayExpr  *fc_newa = makeNode(ArrayExpr);
		int			fc_non_var_args = fc_nargs - fc_nvargs;
		List	   *fc_vargs;

		Assert(fc_non_var_args >= 0);
		fc_vargs = list_copy_tail(fc_fargs, fc_non_var_args);
		fc_fargs = list_truncate(fc_fargs, fc_non_var_args);

		fc_newa->elements = fc_vargs;
		/* 假设所有可变参数都被强制转换为相同的类型 */
		fc_newa->element_typeid = exprType((Node *) linitial(fc_vargs));
		fc_newa->array_typeid = get_array_type(fc_newa->element_typeid);
		if (!OidIsValid(fc_newa->array_typeid))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("could not find array type for data type %s",
							format_type_be(fc_newa->element_typeid)),
					 parser_errposition(fc_pstate, exprLocation((Node *) fc_vargs))));
		/* array_collid 将由 parse_collate.c 设置 */
		fc_newa->multidims = false;
		fc_newa->location = exprLocation((Node *) fc_vargs);

		fc_fargs = lappend(fc_fargs, fc_newa);

		/* 我们之前不可能有可变参数标记... */
		Assert(!fc_func_variadic);
		/* ... 但现在，它是一个可变参数调用 */
		fc_func_variadic = true;
	}

	/* 如果一个“任何”可变参数被显式标记为可变参数，必须坚持可变参数类型为某种数组类型。 */
	if (fc_nargs > 0 && fc_vatype == ANYOID && fc_func_variadic)
	{
		Oid			fc_va_arr_typid = fc_actual_arg_types[fc_nargs - 1];

		if (!OidIsValid(get_base_element_type(fc_va_arr_typid)))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("VARIADIC argument must be an array"),
					 parser_errposition(fc_pstate,
										exprLocation((Node *) llast(fc_fargs)))));
	}

	/* 如果它返回一个集合，请检查这是否可以。 */
	if (fc_retset)
		check_srf_call_placement(fc_pstate, fc_last_srf, fc_location);

	/* 构建适当的输出结构 */
	if (fc_fdresult == FUNCDETAIL_NORMAL || fc_fdresult == FUNCDETAIL_PROCEDURE)
	{
		FuncExpr   *fc_funcexpr = makeNode(FuncExpr);

		fc_funcexpr->funcid = fc_funcid;
		fc_funcexpr->funcresulttype = fc_rettype;
		fc_funcexpr->funcretset = fc_retset;
		fc_funcexpr->funcvariadic = fc_func_variadic;
		fc_funcexpr->funcformat = fc_funcformat;
		/* funccollid 和 inputcollid 将由 parse_collate.c 设置 */
		fc_funcexpr->args = fc_fargs;
		fc_funcexpr->location = fc_location;

		fc_retval = (Node *) fc_funcexpr;
	}
	else if (fc_fdresult == FUNCDETAIL_AGGREGATE && !fc_over)
	{
		/* 聚合函数 */
		Aggref	   *fc_aggref = makeNode(Aggref);

		fc_aggref->aggfnoid = fc_funcid;
		fc_aggref->aggtype = fc_rettype;
		/* aggcollid 和 inputcollid 将由 parse_collate.c 设置 */
		fc_aggref->aggtranstype = InvalidOid;	/* 将由计划者设置 */
		/* aggargtypes 将由 transformAggregateCall 设置 */
		/* aggdirectargs 和 args 将由 transformAggregateCall 设置 */
		/* aggorder 和 aggdistinct 将由 transformAggregateCall 设置 */
		fc_aggref->aggfilter = fc_agg_filter;
		fc_aggref->aggstar = fc_agg_star;
		fc_aggref->aggvariadic = fc_func_variadic;
		fc_aggref->aggkind = fc_aggkind;
		/* agglevelsup 将由 transformAggregateCall 设置 */
		fc_aggref->aggsplit = AGGSPLIT_SIMPLE; /* 计划者可能会更改这个 */
		fc_aggref->aggno = -1;		/* 计划者将设置 aggno 和 aggtransno */
		fc_aggref->aggtransno = -1;
		fc_aggref->location = fc_location;

		/* 拒绝尝试在没有 (*) 语法的情况下调用无参数的聚合。这纯粹是书面上的，但一些人坚持... */
		if (fc_fargs == NIL && !fc_agg_star && !fc_agg_within_group)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("%s(*) must be used to call a parameterless aggregate function",
							NameListToString(fc_funcname)),
					 parser_errposition(fc_pstate, fc_location)));

		if (fc_retset)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("aggregates cannot return sets"),
					 parser_errposition(fc_pstate, fc_location)));

		/*
		 * 我们可能希望稍后支持命名参数，但现在不允许。我们需要弄清楚解析后的表示（NamedArgExprs 应该在 TargetEntry 节点之上还是之下？），然后教计划器正确地重新排列列表。或者也许我们可以让 transformAggregateCall 来做到这一点？不过，如果您还希望允许聚合的默认参数，我们需要在规划中进行处理以避免语义问题。
		 */
		if (fc_argnames != NIL)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("aggregates cannot use named arguments"),
					 parser_errposition(fc_pstate, fc_location)));

		/* parse_agg.c 进行额外的聚合特定处理 */
		transformAggregateCall(fc_pstate, fc_aggref, fc_fargs, fc_agg_order, fc_agg_distinct);

		fc_retval = (Node *) fc_aggref;
	}
	else
	{
		/* 窗口函数 */
		WindowFunc *fc_wfunc = makeNode(WindowFunc);

		Assert(fc_over);			/* 上面已检查此缺失 */
		Assert(!fc_agg_within_group);	/* 也在上面进行了检查 */

		fc_wfunc->winfnoid = fc_funcid;
		fc_wfunc->wintype = fc_rettype;
		/* wincollid 和 inputcollid 将由 parse_collate.c 设置 */
		fc_wfunc->args = fc_fargs;
		/* winref 将由 transformWindowFuncCall 设置 */
		fc_wfunc->winstar = fc_agg_star;
		fc_wfunc->winagg = (fc_fdresult == FUNCDETAIL_AGGREGATE);
		fc_wfunc->aggfilter = fc_agg_filter;
		fc_wfunc->location = fc_location;

		/*
		 * agg_star 对聚合函数是允许的，但 distinct 不允许
		 */
		if (fc_agg_distinct)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("DISTINCT is not implemented for window functions"),
					 parser_errposition(fc_pstate, fc_location)));

		/* 拒绝尝试在没有 (*) 语法的情况下调用无参数的聚合。这纯粹是书面上的，但一些人坚持... */
		if (fc_wfunc->winagg && fc_fargs == NIL && !fc_agg_star)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("%s(*) must be used to call a parameterless aggregate function",
							NameListToString(fc_funcname)),
					 parser_errposition(fc_pstate, fc_location)));

		/*
		 * 在窗口中尚不允许有序聚合
		 */
		if (fc_agg_order != NIL)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("aggregate ORDER BY is not implemented for window functions"),
					 parser_errposition(fc_pstate, fc_location)));

		/*
		 * 目前尚不支持与真实窗口函数的 FILTER
		 */
		if (!fc_wfunc->winagg && fc_agg_filter)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("FILTER is not implemented for non-aggregate window functions"),
					 parser_errposition(fc_pstate, fc_location)));

		/*
		 * 窗口函数既不能接受也不能返回集合
		 */
		if (fc_pstate->p_last_srf != fc_last_srf)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("window function calls cannot contain set-returning function calls"),
					 errhint("You might be able to move the set-returning function into a LATERAL FROM item."),
					 parser_errposition(fc_pstate,
										exprLocation(fc_pstate->p_last_srf))));

		if (fc_retset)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("window functions cannot return sets"),
					 parser_errposition(fc_pstate, fc_location)));

		/* parse_agg.c 进行额外的窗口函数特定处理 */
		transformWindowFuncCall(fc_pstate, fc_wfunc, fc_over);

		fc_retval = (Node *) fc_wfunc;
	}

	/* 如果返回一个集合，请在更高层次的错误检查中记住它 */
	if (fc_retset)
		fc_pstate->p_last_srf = fc_retval;

	return fc_retval;
}


/* func_match_argtypes()
 *
 * 给定具有正确名称和参数数量的候选函数列表和输入数据类型 OID 数组，生成一个实际上接受输入数据类型（无论是完全相同还是通过类型转换）的候选函数的简短列表，并返回此类候选函数的数量。
 *
 * 请注意，can_coerce_type 将假设 UNKNOWN 输入可以转换为任何类型，因此候选函数不会因此被淘汰。
 *
 * 注意：只要找到至少一个匹配项，可以修改输入列表结构。如果完全没有匹配项，则列表必须保持不变。
 */
int func_match_argtypes(int fc_nargs,
					Oid *fc_input_typeids,
					FuncCandidateList fc_raw_candidates,
					FuncCandidateList *fc_candidates)	/* 返回值 */
{
	FuncCandidateList fc_current_candidate;
	FuncCandidateList fc_next_candidate;
	int			fc_ncandidates = 0;

	*fc_candidates = NULL;

	for (fc_current_candidate = fc_raw_candidates;
		 fc_current_candidate != NULL;
		 fc_current_candidate = fc_next_candidate)
	{
		fc_next_candidate = fc_current_candidate->next;
		if (can_coerce_type(fc_nargs, fc_input_typeids, fc_current_candidate->args,
							COERCION_IMPLICIT))
		{
			fc_current_candidate->next = *fc_candidates;
			*fc_candidates = fc_current_candidate;
			fc_ncandidates++;
		}
	}

	return fc_ncandidates;
}								/* func_match_argtypes() */



/* func_select_candidate（）
 * 给定输入argtype数组和多个候选函数，尝试解决冲突。
 *
 * 如果可以解决冲突，则返回选定的候选者，否则返回NULL。
 *
 * 注意，调用者已经确定没有与输入argtypes完全匹配的候选者，并且已剔除了任何与输入类型实际上不兼容的“候选者”。
 *
 * 这也用于解决模糊的操作符引用。以前parse_oper.c有自己基本上重复的代码来实现这个目的。
 * 以下评论（以前在parse_oper.c中）保留是为了记录这些启发式方法的一些历史。
 *
 * 旧评论：
 *
 * 这个例程是新代码，替代了binary_oper_select_candidate（）
 * 自v4.2/v1.0.x以来。它非常努力地将操作符与类型匹配，包括在必要时允许类型强制转换。
 * 重要的是，这段代码尽可能多地完成工作，同时_绝不_做错事，其中“错误的事情”将是返回一个操作符，而其他更好的选择可用，
 * 或返回一个非直观的操作符。
 * - thomas 1998-05-21
 *
 * 下面的评论来自binary_oper_select_candidate（），并
 * 说明了可能的各种问题和选择：
 * - thomas 1998-05-20
 *
 * 当前的智慧认为，默认操作符应该是两个操作数具有相同类型的操作符（将只有一个这样的操作符）
 *
 * 1993年7月27日 - 我决定不这样做；这很难证明，显式类型转换也很简单 - avi
 * [此例程的其余部分从那时起被注释掉了 - ay]
 *
 * 1995年6月23日 - 我不完全同意avi。特别是，转换浮点数对用户来说很麻烦。不管不这样做的理由是什么，我需要以下特殊情况工作。
 *
 * 在查询的WHERE子句中，如果指定了一个没有引号的浮点数，我们将其视为float8。我添加了float48*操作符，以便我们可以对float4和float8进行操作。但如果右侧参数未知（例如，带引号指定的浮点数），我们就有了多个匹配操作符。这破坏了回归测试中的一些内容，其中有浮点数未正确转换为引号。下面是解决方案。除了要求操作符在两个操作数上操作相同类型[如Avi最初注释掉的代码中所示]，我们还要求操作符在某种意义上是等效的。（有关详细信息，请参见equivalentOpersAfterPromotion。）
 * - ay 6/95
 */
FuncCandidateList func_select_candidate(int fc_nargs,
					  Oid *fc_input_typeids,
					  FuncCandidateList fc_candidates)
{
	FuncCandidateList fc_current_candidate,
				fc_first_candidate,
				fc_last_candidate;
	Oid		   *fc_current_typeids;
	Oid			fc_current_type;
	int			fc_i;
	int			fc_ncandidates;
	int			fc_nbestMatch,
				fc_nmatch,
				fc_nunknowns;
	Oid			fc_input_base_typeids[FUNC_MAX_ARGS];
	TYPCATEGORY fc_slot_category[FUNC_MAX_ARGS],
				fc_current_category;
	bool		fc_current_is_preferred;
	bool		fc_slot_has_preferred_type[FUNC_MAX_ARGS];
	bool		fc_resolved_unknowns;

	/* 保护局部固定大小数组 */
	if (fc_nargs > FUNC_MAX_ARGS)
		ereport(ERROR,
				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
				 errmsg_plural("cannot pass more than %d argument to a function",
							   "cannot pass more than %d arguments to a function",
							   FUNC_MAX_ARGS,
							   FUNC_MAX_ARGS)));

	/*
	 * 如果任何输入类型是域，则将其减少到其基本类型。这
	 * 确保我们将基本类型上的函数视为“精确匹配”
	 * 在精确匹配启发式中；这也使得能够对
	 * 类型类别启发式做一些有用的事情。注意，这使得
	 * 使用声明为以域作为输入数据类型的函数变得困难，但并非不可能。
	 * 只有在所有参数位置完全匹配时，才会选择这样的函数
	 * 越过基本类型函数，因此已经由我们的调用者选择。
	 *
	 * 在此期间，计算未知类型参数的数量以备后用。
	 */
	fc_nunknowns = 0;
	for (fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		if (fc_input_typeids[fc_i] != UNKNOWNOID)
			fc_input_base_typeids[fc_i] = getBaseType(fc_input_typeids[fc_i]);
		else
		{
			/* 对于UNKNOWNOID，没有必要调用getBaseType */
			fc_input_base_typeids[fc_i] = UNKNOWNOID;
			fc_nunknowns++;
		}
	}

	/*
	 * 遍历所有候选者，保留在
	 * 精确类型上最匹配的。 如果没有匹配，则保留所有候选者。
	 */
	fc_ncandidates = 0;
	fc_nbestMatch = 0;
	fc_last_candidate = NULL;
	for (fc_current_candidate = fc_candidates;
		 fc_current_candidate != NULL;
		 fc_current_candidate = fc_current_candidate->next)
	{
		fc_current_typeids = fc_current_candidate->args;
		fc_nmatch = 0;
		for (fc_i = 0; fc_i < fc_nargs; fc_i++)
		{
			if (fc_input_base_typeids[fc_i] != UNKNOWNOID &&
				fc_current_typeids[fc_i] == fc_input_base_typeids[fc_i])
				fc_nmatch++;
		}

		/* 这一项作为到目前为止的最佳选择？ */
		if ((fc_nmatch > fc_nbestMatch) || (fc_last_candidate == NULL))
		{
			fc_nbestMatch = fc_nmatch;
			fc_candidates = fc_current_candidate;
			fc_last_candidate = fc_current_candidate;
			fc_ncandidates = 1;
		}
		/* 不比上一个选择差，所以也保留这一项？ */
		else if (fc_nmatch == fc_nbestMatch)
		{
			fc_last_candidate->next = fc_current_candidate;
			fc_last_candidate = fc_current_candidate;
			fc_ncandidates++;
		}
		/* 否则，不用保留这一项... */
	}

	if (fc_last_candidate)			/* 终止重建列表 */
		fc_last_candidate->next = NULL;

	if (fc_ncandidates == 1)
		return fc_candidates;

	/*
	 * 仍然候选人太多？现在寻找具有完全匹配或在需要强制转换的参数上具有首选类型的候选人。 (在7.4中添加的限制：首选类型必须与输入类型属于同一类别；对跨类别的首选类型转换没有优先权。) 如果没有匹配，则保留所有候选人。
	 */
	for (fc_i = 0; fc_i < fc_nargs; fc_i++) /* 避免多次查找 */
		fc_slot_category[fc_i] = TypeCategory(fc_input_base_typeids[fc_i]);
	fc_ncandidates = 0;
	fc_nbestMatch = 0;
	fc_last_candidate = NULL;
	for (fc_current_candidate = fc_candidates;
		 fc_current_candidate != NULL;
		 fc_current_candidate = fc_current_candidate->next)
	{
		fc_current_typeids = fc_current_candidate->args;
		fc_nmatch = 0;
		for (fc_i = 0; fc_i < fc_nargs; fc_i++)
		{
			if (fc_input_base_typeids[fc_i] != UNKNOWNOID)
			{
				if (fc_current_typeids[fc_i] == fc_input_base_typeids[fc_i] ||
					IsPreferredType(fc_slot_category[fc_i], fc_current_typeids[fc_i]))
					fc_nmatch++;
			}
		}

		if ((fc_nmatch > fc_nbestMatch) || (fc_last_candidate == NULL))
		{
			fc_nbestMatch = fc_nmatch;
			fc_candidates = fc_current_candidate;
			fc_last_candidate = fc_current_candidate;
			fc_ncandidates = 1;
		}
		else if (fc_nmatch == fc_nbestMatch)
		{
			fc_last_candidate->next = fc_current_candidate;
			fc_last_candidate = fc_current_candidate;
			fc_ncandidates++;
		}
	}

	if (fc_last_candidate)			/* 终止重建列表 */
		fc_last_candidate->next = NULL;

	if (fc_ncandidates == 1)
		return fc_candidates;

	/*
	 * 仍然候选人太多？尝试为未知输入分配类型。
	 *
	 * 如果没有未知输入，我们没有更多的启发式应用，
	 * 必须失败。
	 */
	if (fc_nunknowns == 0)
		return NULL;			/* 未能选择最佳候选人 */

	/*
	 * 下一步检查每个未知参数位置，以查看我们是否可以
	 * 确定“类型类别”。如果任何候选人具有字符串类别的输入
	 * 数据类型，则使用字符串类别（这种偏向于字符串是合适的，因为未知类型的字面量看起来像字符串）。
	 * 否则，如果所有候选人都同意这个参数位置的类型类别，则使用该类别。否则，失败，因为我们
	 * 无法确定一个类别。
	 *
	 * 如果我们能够确定一个类型类别，还要注意任何候选人是否在类别内使用了首选数据类型。
	 *
	 * 完成这一检查后，删除在任何未知位置接受错误类别的候选人。如果至少有一个
	 * 候选人在某个位置接受了首选类型，则删除接受非首选类型的候选人。如果只剩下一个候选人，则返回
	 * 该候选人。但是，如果此规则导致拒绝所有候选人，请保留它们。
	 */
	fc_resolved_unknowns = false;
	for (fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		bool		fc_have_conflict;

		if (fc_input_base_typeids[fc_i] != UNKNOWNOID)
			continue;
		fc_resolved_unknowns = true;	/* 假设我们可以做到 */
		fc_slot_category[fc_i] = TYPCATEGORY_INVALID;
		fc_slot_has_preferred_type[fc_i] = false;
		fc_have_conflict = false;
		for (fc_current_candidate = fc_candidates;
			 fc_current_candidate != NULL;
			 fc_current_candidate = fc_current_candidate->next)
		{
			fc_current_typeids = fc_current_candidate->args;
			fc_current_type = fc_current_typeids[fc_i];
			get_type_category_preferred(fc_current_type,
										&fc_current_category,
										&fc_current_is_preferred);
			if (fc_slot_category[fc_i] == TYPCATEGORY_INVALID)
			{
				/* 第一个候选人 */
				fc_slot_category[fc_i] = fc_current_category;
				fc_slot_has_preferred_type[fc_i] = fc_current_is_preferred;
			}
			else if (fc_current_category == fc_slot_category[fc_i])
			{
				/* 同一类别中的更多候选人 */
				fc_slot_has_preferred_type[fc_i] |= fc_current_is_preferred;
			}
			else
			{
				/* 类别冲突！ */
				if (fc_current_category == TYPCATEGORY_STRING)
				{
					/* 如果可用，STRING 始终胜出 */
					fc_slot_category[fc_i] = fc_current_category;
					fc_slot_has_preferred_type[fc_i] = fc_current_is_preferred;
				}
				else
				{
					/*
					 * 记住冲突，但继续进行（可能会找到STRING）
					 */
					fc_have_conflict = true;
				}
			}
		}
		if (fc_have_conflict && fc_slot_category[fc_i] != TYPCATEGORY_STRING)
		{
			/* 未能解决该位置的类别冲突 */
			fc_resolved_unknowns = false;
			break;
		}
	}

	if (fc_resolved_unknowns)
	{
		/* 删除不匹配的候选人 */
		fc_ncandidates = 0;
		fc_first_candidate = fc_candidates;
		fc_last_candidate = NULL;
		for (fc_current_candidate = fc_candidates;
			 fc_current_candidate != NULL;
			 fc_current_candidate = fc_current_candidate->next)
		{
			bool		fc_keepit = true;

			fc_current_typeids = fc_current_candidate->args;
			for (fc_i = 0; fc_i < fc_nargs; fc_i++)
			{
				if (fc_input_base_typeids[fc_i] != UNKNOWNOID)
					continue;
				fc_current_type = fc_current_typeids[fc_i];
				get_type_category_preferred(fc_current_type,
											&fc_current_category,
											&fc_current_is_preferred);
				if (fc_current_category != fc_slot_category[fc_i])
				{
					fc_keepit = false;
					break;
				}
				if (fc_slot_has_preferred_type[fc_i] && !fc_current_is_preferred)
				{
					fc_keepit = false;
					break;
				}
			}
			if (fc_keepit)
			{
				/* 保留此候选人 */
				fc_last_candidate = fc_current_candidate;
				fc_ncandidates++;
			}
			else
			{
				/* 忘记此候选人 */
				if (fc_last_candidate)
					fc_last_candidate->next = fc_current_candidate->next;
				else
					fc_first_candidate = fc_current_candidate->next;
			}
		}

		/* 如果我们找到任何匹配项，将我们的关注范围限制为那些 */
		if (fc_last_candidate)
		{
			fc_candidates = fc_first_candidate;
			/* 终止重建列表 */
			fc_last_candidate->next = NULL;
		}

		if (fc_ncandidates == 1)
			return fc_candidates;
	}

	/*
	 * 最后一次呼吸：如果同时存在已知类型和未知类型输入，且所有
	 * 已知类型相同，假设未知输入也是该
	 * 类型，并查看这是否给我们提供了唯一匹配。如果是这样，请使用该匹配。
	 *
	 * 注意：对于一个具有一个未知和一个非未知输入的二元运算符，
	 * 我们已经在 binary_oper_exact() 中尝试过这个启发式。 然而，那个
	 * 代码仅找到精确匹配，而在这里我们将处理涉及强制转换、多态类型解析等的匹配。
	 */
	if (fc_nunknowns < fc_nargs)
	{
		Oid			fc_known_type = UNKNOWNOID;

		for (fc_i = 0; fc_i < fc_nargs; fc_i++)
		{
			if (fc_input_base_typeids[fc_i] == UNKNOWNOID)
				continue;
			if (fc_known_type == UNKNOWNOID)	/* 第一个已知参数？ */
				fc_known_type = fc_input_base_typeids[fc_i];
			else if (fc_known_type != fc_input_base_typeids[fc_i])
			{
				/* 哎呀，并非所有都匹配 */
				fc_known_type = UNKNOWNOID;
				break;
			}
		}

		if (fc_known_type != UNKNOWNOID)
		{
			/* 好吧，只有一种已知类型，应用启发式 */
			for (fc_i = 0; fc_i < fc_nargs; fc_i++)
				fc_input_base_typeids[fc_i] = fc_known_type;
			fc_ncandidates = 0;
			fc_last_candidate = NULL;
			for (fc_current_candidate = fc_candidates;
				 fc_current_candidate != NULL;
				 fc_current_candidate = fc_current_candidate->next)
			{
				fc_current_typeids = fc_current_candidate->args;
				if (can_coerce_type(fc_nargs, fc_input_base_typeids, fc_current_typeids,
									COERCION_IMPLICIT))
				{
					if (++fc_ncandidates > 1)
						break;	/* 不是唯一的，放弃 */
					fc_last_candidate = fc_current_candidate;
				}
			}
			if (fc_ncandidates == 1)
			{
				/* 成功识别出唯一匹配 */
				fc_last_candidate->next = NULL;
				return fc_last_candidate;
			}
		}
	}

	return NULL;				/* 未能选择最佳候选人 */
}								/* func_select_candidate() */


/* func_get_detail()
 *
 * 在系统目录中查找命名函数。
 *
 * 尝试在系统目录中查找命名函数，参数正好如指定的那样，
 * 以便正常情况（精确匹配）尽可能快速。
 *
 * 如果未找到精确匹配：
 *	1）检查是否可以解释为类型强制转换请求
 *	2）应用模糊函数解析规则
 *
 * 返回值 *funcid 通过 *true_typeids 接收关于函数的信息。
 * 如果 argdefaults 不是 NULL，*argdefaults 接收需要
 * 添加到给定参数的默认参数表达式列表。
 *
 * 在处理命名或混合符号调用时（即 fargnames 不是 NIL），
 * 返回的 true_typeids 和 argdefaults 根据
 * 调用的参数顺序进行排序：首先是任何位置参数，
 * 然后是命名参数，然后是默认参数（如果需要且允许
 * expand_defaults 的话）。如果想将此信息与函数的 pg_proc 条目进行比较，则需要特别小心，
 * 但实际上调用者通常可以简单地按调用的参数顺序进行处理。
 *
 * 我们主要依赖 fargnames/nargs/argtypes 作为参数描述。
 * 实际的表达式节点列表在 fargs 中传递，以便我们可以检查
 * 对常量的类型强制转换。一些调用者将 fargs == NIL，表示
 * 他们不需要进行该检查。请注意，当 fargnames 不是 NIL 时，
 * 如果调用者希望实际的参数位置
 * 信息返回到 NamedArgExpr 节点，则必须传递 fargs 列表。
 */
FuncDetailCode
func_get_detail(List *fc_funcname,
				List *fc_fargs,
				List *fc_fargnames,
				int fc_nargs,
				Oid *fc_argtypes,
				bool fc_expand_variadic,
				bool fc_expand_defaults,
				bool fc_include_out_arguments,
				Oid *fc_funcid,	/* 返回值 */
				Oid *fc_rettype,	/* 返回值 */
				bool *fc_retset,	/* 返回值 */
				int *fc_nvargs,	/* 返回值 */
				Oid *fc_vatype,	/* 返回值 */
				Oid **fc_true_typeids, /* 返回值 */
				List **fc_argdefaults) /* 可选返回值 */
{
	FuncCandidateList fc_raw_candidates;
	FuncCandidateList fc_best_candidate;

	/* 初始化输出参数以消除编译器警告 */
	*fc_funcid = InvalidOid;
	*fc_rettype = InvalidOid;
	*fc_retset = false;
	*fc_nvargs = 0;
	*fc_vatype = InvalidOid;
	*fc_true_typeids = NULL;
	if (fc_argdefaults)
		*fc_argdefaults = NIL;

	/* 从命名空间搜索获取可能候选人列表 */
	fc_raw_candidates = FuncnameGetCandidates(fc_funcname, fc_nargs, fc_fargnames,
										   fc_expand_variadic, fc_expand_defaults,
										   fc_include_out_arguments, false);

	/*
	 * 快速检查输入数据类型是否存在精确匹配（只能有一个）
	 */
	for (fc_best_candidate = fc_raw_candidates;
		 fc_best_candidate != NULL;
		 fc_best_candidate = fc_best_candidate->next)
	{
		/* 如果 nargs==0，argtypes 可以为 null；不要将其传递给 memcmp */
		if (fc_nargs == 0 ||
			memcmp(fc_argtypes, fc_best_candidate->args, fc_nargs * sizeof(Oid)) == 0)
			break;
	}

	if (fc_best_candidate == NULL)
	{
		
		if (fc_nargs == 1 && fc_fargs != NIL && fc_fargnames == NIL)
		{
			Oid			fc_targetType = fc_FuncNameAsType(fc_funcname);

			if (OidIsValid(fc_targetType))
			{
				Oid			fc_sourceType = fc_argtypes[0];
				Node	   *fc_arg1 = linitial(fc_fargs);
				bool		fc_iscoercion;

				if (fc_sourceType == UNKNOWNOID && IsA(fc_arg1, Const))
				{
					/* 始终将 typename('literal') 视为类型强制 */
					fc_iscoercion = true;
				}
				else
				{
					CoercionPathType fc_cpathtype;
					Oid			fc_cfuncid;

					fc_cpathtype = find_coercion_pathway(fc_targetType, fc_sourceType,
													  COERCION_EXPLICIT,
													  &fc_cfuncid);
					switch (fc_cpathtype)
					{
						case COERCION_PATH_RELABELTYPE:
							fc_iscoercion = true;
							break;
						case COERCION_PATH_COERCEVIAIO:
							if ((fc_sourceType == RECORDOID ||
								 ISCOMPLEX(fc_sourceType)) &&
								TypeCategory(fc_targetType) == TYPCATEGORY_STRING)
								fc_iscoercion = false;
							else
								fc_iscoercion = true;
							break;
						default:
							fc_iscoercion = false;
							break;
					}
				}

				if (fc_iscoercion)
				{
					/* 将其视为类型强制 */
					*fc_funcid = InvalidOid;
					*fc_rettype = fc_targetType;
					*fc_retset = false;
					*fc_nvargs = 0;
					*fc_vatype = InvalidOid;
					*fc_true_typeids = fc_argtypes;
					return FUNCDETAIL_COERCION;
				}
			}
		}

		/*
		 * 未找到准确匹配项，因此现在尝试匹配候选项...
		 */
		if (fc_raw_candidates != NULL)
		{
			FuncCandidateList fc_current_candidates;
			int			fc_ncandidates;

			fc_ncandidates = func_match_argtypes(fc_nargs,
											  fc_argtypes,
											  fc_raw_candidates,
											  &fc_current_candidates);

			/* 仅有一个匹配？那么就运行它... */
			if (fc_ncandidates == 1)
				fc_best_candidate = fc_current_candidates;

			/*
			 * 多个候选项？那么最好决定或抛出错误...
			 */
			else if (fc_ncandidates > 1)
			{
				fc_best_candidate = func_select_candidate(fc_nargs,
													   fc_argtypes,
													   fc_current_candidates);

				/*
				 * 如果我们能够选择最佳候选项，那么就完成了。
				 * 否则，函数调用模糊不清。
				 */
				if (!fc_best_candidate)
					return FUNCDETAIL_MULTIPLE;
			}
		}
	}

	if (fc_best_candidate)
	{
		HeapTuple	fc_ftup;
		Form_pg_proc fc_pform;
		FuncDetailCode fc_result;

		/*
		 * 如果处理命名参数或展开可变参数或默认值，则
		 * “最佳候选项”可能表示多个同样好的
		 * 函数；将此情况视为模糊。
		 */
		if (!OidIsValid(fc_best_candidate->oid))
			return FUNCDETAIL_MULTIPLE;

		/*
		 * 除非最后一个参数（带有 VARIADIC 的参数）实际匹配了可变
		 * 参数，否则我们不允许 VARIADIC 与命名参数一起使用。 这实际上只是严格，但一些人坚持这样做。
		 */
		if (fc_fargnames != NIL && !fc_expand_variadic && fc_nargs > 0 &&
			fc_best_candidate->argnumbers[fc_nargs - 1] != fc_nargs - 1)
			return FUNCDETAIL_NOTFOUND;

		*fc_funcid = fc_best_candidate->oid;
		*fc_nvargs = fc_best_candidate->nvargs;
		*fc_true_typeids = fc_best_candidate->args;

		/*
		 * 如果处理命名参数，则将实际参数位置返回到
		 * fargs 列表中的 NamedArgExpr 节点中。 这有点丑，但
		 * 不值得额外的符号来以不同的方式处理它。
		 */
		if (fc_best_candidate->argnumbers != NULL)
		{
			int			fc_i = 0;
			ListCell   *fc_lc;

			foreach(fc_lc, fc_fargs)
			{
				NamedArgExpr *fc_na = (NamedArgExpr *) lfirst(fc_lc);

				if (IsA(fc_na, NamedArgExpr))
					fc_na->argnumber = fc_best_candidate->argnumbers[fc_i];
				fc_i++;
			}
		}

		fc_ftup = SearchSysCache1(PROCOID,
							   ObjectIdGetDatum(fc_best_candidate->oid));
		if (!HeapTupleIsValid(fc_ftup))	/* 不应该发生 */
			elog(ERROR, "cache lookup failed for function %u",
				 fc_best_candidate->oid);
		fc_pform = (Form_pg_proc) GETSTRUCT(fc_ftup);
		*fc_rettype = fc_pform->prorettype;
		*fc_retset = fc_pform->proretset;
		*fc_vatype = fc_pform->provariadic;
		/* 如果调用者想要默认参数，则获取默认参数 */
		if (fc_argdefaults && fc_best_candidate->ndargs > 0)
		{
			Datum		fc_proargdefaults;
			bool		fc_isnull;
			char	   *fc_str;
			List	   *fc_defaults;

			/* 不应该发生，FuncnameGetCandidates 出错了 */
			if (fc_best_candidate->ndargs > fc_pform->pronargdefaults)
				elog(ERROR, "not enough default arguments");

			fc_proargdefaults = SysCacheGetAttr(PROCOID, fc_ftup,
											 Anum_pg_proc_proargdefaults,
											 &fc_isnull);
			Assert(!fc_isnull);
			fc_str = TextDatumGetCString(fc_proargdefaults);
			fc_defaults = castNode(List, stringToNode(fc_str));
			pfree(fc_str);

			/* 从返回列表中删除任何未使用的默认值 */
			if (fc_best_candidate->argnumbers != NULL)
			{
				/*
				 * 在命名符号中这有点棘手，因为提供的
				 * 参数可能替换默认参数的任何子集。 我们
				 * 通过制作默认参数的 argnumbers 位图集来工作，
				 * 然后扫描默认值列表并选择所需项。 （这假设默认参数
				 * 应按其位置顺序提供。）
				 */
				Bitmapset  *fc_defargnumbers;
				int		   *fc_firstdefarg;
				List	   *fc_newdefaults;
				ListCell   *fc_lc;
				int			fc_i;

				fc_defargnumbers = NULL;
				fc_firstdefarg = &fc_best_candidate->argnumbers[fc_best_candidate->nargs - fc_best_candidate->ndargs];
				for (fc_i = 0; fc_i < fc_best_candidate->ndargs; fc_i++)
					fc_defargnumbers = bms_add_member(fc_defargnumbers,
												   fc_firstdefarg[fc_i]);
				fc_newdefaults = NIL;
				fc_i = fc_best_candidate->nominalnargs - fc_pform->pronargdefaults;
				foreach(fc_lc, fc_defaults)
				{
					if (bms_is_member(fc_i, fc_defargnumbers))
						fc_newdefaults = lappend(fc_newdefaults, lfirst(fc_lc));
					fc_i++;
				}
				Assert(list_length(fc_newdefaults) == fc_best_candidate->ndargs);
				bms_free(fc_defargnumbers);
				*fc_argdefaults = fc_newdefaults;
			}
			else
			{
				/*
				 * 对于位置符号的默认值要简单得多；只需
				 * 从前面删除任何不需要的。
				 */
				int			fc_ndelete;

				fc_ndelete = list_length(fc_defaults) - fc_best_candidate->ndargs;
				if (fc_ndelete > 0)
					fc_defaults = list_delete_first_n(fc_defaults, fc_ndelete);
				*fc_argdefaults = fc_defaults;
			}
		}

		switch (fc_pform->prokind)
		{
			case PROKIND_AGGREGATE:
				fc_result = FUNCDETAIL_AGGREGATE;
				break;
			case PROKIND_FUNCTION:
				fc_result = FUNCDETAIL_NORMAL;
				break;
			case PROKIND_PROCEDURE:
				fc_result = FUNCDETAIL_PROCEDURE;
				break;
			case PROKIND_WINDOW:
				fc_result = FUNCDETAIL_WINDOWFUNC;
				break;
			default:
				elog(ERROR, "unrecognized prokind: %c", fc_pform->prokind);
				fc_result = FUNCDETAIL_NORMAL; /* 保持编译器安静 */
				break;
		}

		ReleaseSysCache(fc_ftup);
		return fc_result;
	}

	return FUNCDETAIL_NOTFOUND;
}


/*
 * unify_hypothetical_args()
 *
 * 确保假设集合的每个直接参数与相应的聚合参数具有相同的类型。
 * 如果必要，修改 fargs 列表中的表达式，并更新
 * actual_arg_types[]。
 *
 * 如果聚合声明其参数为非-ANY（甚至是 ANYELEMENT），我们只需要
 * 简单检查声明的类型是否匹配；make_fn_arguments 将强制
 * 实际参数与声明的参数匹配。 但是，如果声明是 ANY，则在 make_fn_arguments 中将不会发生任何事情，
 * 因此我们需要在这里修复任何不匹配。 我们使用与 UNION 等相同的
 * 类型解析逻辑。
 */
static void fc_unify_hypothetical_args(ParseState *fc_pstate,
						List *fc_fargs,
						int fc_numAggregatedArgs,
						Oid *fc_actual_arg_types,
						Oid *fc_declared_arg_types)
{
	int			fc_numDirectArgs,
				fc_numNonHypotheticalArgs;
	int			fc_hargpos;

	fc_numDirectArgs = list_length(fc_fargs) - fc_numAggregatedArgs;
	fc_numNonHypotheticalArgs = fc_numDirectArgs - fc_numAggregatedArgs;
	/* 安全检查（只应在错误声明的聚合时触发） */
	if (fc_numNonHypotheticalArgs < 0)
		elog(ERROR, "incorrect number of arguments to hypothetical-set aggregate");

	/* 检查每个假设参数和相应的聚合参数 */
	for (fc_hargpos = fc_numNonHypotheticalArgs; fc_hargpos < fc_numDirectArgs; fc_hargpos++)
	{
		int			fc_aargpos = fc_numDirectArgs + (fc_hargpos - fc_numNonHypotheticalArgs);
		ListCell   *fc_harg = list_nth_cell(fc_fargs, fc_hargpos);
		ListCell   *fc_aarg = list_nth_cell(fc_fargs, fc_aargpos);
		Oid			fc_commontype;
		int32		fc_commontypmod;

		/* 不匹配意味着 AggregateCreate 没有正确检查... */
		if (fc_declared_arg_types[fc_hargpos] != fc_declared_arg_types[fc_aargpos])
			elog(ERROR, "hypothetical-set aggregate has inconsistent declared argument types");

		/* 如果 make_fn_arguments 会强制转换，则无需统一 */
		if (fc_declared_arg_types[fc_hargpos] != ANYOID)
			continue;

		/*
		 * 选择共同类型，优先考虑聚合参数的
		 * 类型（我们宁愿一次强制直接参数，而不是强制所有
		 * 聚合值）。
		 */
		fc_commontype = select_common_type(fc_pstate,
										list_make2(lfirst(fc_aarg), lfirst(fc_harg)),
										"WITHIN GROUP",
										NULL);
		fc_commontypmod = select_common_typmod(fc_pstate,
											list_make2(lfirst(fc_aarg), lfirst(fc_harg)),
											fc_commontype);

		/*
		 * 执行强制转换。我们不需要担心 NamedArgExprs
		 * 因为它们在聚合中不受支持。
		 */
		lfirst(fc_harg) = coerce_type(fc_pstate,
								   (Node *) lfirst(fc_harg),
								   fc_actual_arg_types[fc_hargpos],
								   fc_commontype, fc_commontypmod,
								   COERCION_IMPLICIT,
								   COERCE_IMPLICIT_CAST,
								   -1);
		fc_actual_arg_types[fc_hargpos] = fc_commontype;
		lfirst(fc_aarg) = coerce_type(fc_pstate,
								   (Node *) lfirst(fc_aarg),
								   fc_actual_arg_types[fc_aargpos],
								   fc_commontype, fc_commontypmod,
								   COERCION_IMPLICIT,
								   COERCE_IMPLICIT_CAST,
								   -1);
		fc_actual_arg_types[fc_aargpos] = fc_commontype;
	}
}


/*
 * make_fn_arguments()
 *
 * 给定函数的实际参数表达式和所需的
 * 输入类型，为表达式树添加任何必要的类型转换。
 * 调用者应已验证允许转换。
 *
 * 注意：给定的参数列表在原地修改。
 *
 * 与 coerce_type 一样，如果不希望进行特殊的未知-Param
 * 处理，pstate 可以为 NULL。
 */
void make_fn_arguments(ParseState *fc_pstate,
				  List *fc_fargs,
				  Oid *fc_actual_arg_types,
				  Oid *fc_declared_arg_types)
{
	ListCell   *fc_current_fargs;
	int			fc_i = 0;

	foreach(fc_current_fargs, fc_fargs)
	{
		/* 类型不匹配？那么通过函数调用强制转换... */
		if (fc_actual_arg_types[fc_i] != fc_declared_arg_types[fc_i])
		{
			Node	   *fc_node = (Node *) lfirst(fc_current_fargs);

			/*
			 * 如果参数是 NamedArgExpr，则强制转换其输入表达式——我们
			 * 想要 NamedArgExpr 保持在列表的顶层。
			 */
			if (IsA(fc_node, NamedArgExpr))
			{
				NamedArgExpr *fc_na = (NamedArgExpr *) fc_node;

				fc_node = coerce_type(fc_pstate,
								   (Node *) fc_na->arg,
								   fc_actual_arg_types[fc_i],
								   fc_declared_arg_types[fc_i], -1,
								   COERCION_IMPLICIT,
								   COERCE_IMPLICIT_CAST,
								   -1);
				fc_na->arg = (Expr *) fc_node;
			}
			else
			{
				fc_node = coerce_type(fc_pstate,
								   fc_node,
								   fc_actual_arg_types[fc_i],
								   fc_declared_arg_types[fc_i], -1,
								   COERCION_IMPLICIT,
								   COERCE_IMPLICIT_CAST,
								   -1);
				lfirst(fc_current_fargs) = fc_node;
			}
		}
		fc_i++;
	}
}

/*
 * FuncNameAsType -
 *	  方便的例程，用于查看函数名是否与类型名匹配
 *
 * 返回匹配类型的 OID，如果没有则返回 InvalidOid。 我们忽略
 * 外壳类型和复合类型。
 */
static Oid fc_FuncNameAsType(List *fc_funcname)
{
	Oid			fc_result;
	Type		fc_typtup;

	/*
	 * temp_ok=false 保护 <refsect1 id="sql-createfunction-security">
	 * 合同，以安全地编写 SECURITY DEFINER 函数。
	 */
	fc_typtup = LookupTypeNameExtended(NULL, makeTypeNameFromNameList(fc_funcname),
									NULL, false, false);
	if (fc_typtup == NULL)
		return InvalidOid;

	if (((Form_pg_type) GETSTRUCT(fc_typtup))->typisdefined &&
		!OidIsValid(typeTypeRelid(fc_typtup)))
		fc_result = typeTypeId(fc_typtup);
	else
		fc_result = InvalidOid;

	ReleaseSysCache(fc_typtup);
	return fc_result;
}

/*
 * ParseComplexProjection -
 *	  处理具有复合类型的单个参数的函数调用。
 *	  如果函数调用实际上是列投影，则返回适当
 *	  转换的表达式树。如果不是，则返回 NULL。
 */
static Node * fc_ParseComplexProjection(ParseState *fc_pstate, const char *fc_funcname, Node *fc_first_arg,
					   int fc_location)
{
	TupleDesc	fc_tupdesc;
	int			fc_i;

	/*
	 * Whole-row Vars 的特殊情况，以便我们可以解析 (foo.*).bar，即使
	 * foo 是对子选择、连接或 RECORD 函数的引用。一个额外的优点是避免
	 * 生成不必要的 FieldSelect；我们的结果可以省略整个行变量，直接作为所选
	 * 字段的 Var。
	 *
	 * 这个情况可以通过 expandRecordVariable 处理，但这样做更有效。
	 */
	if (IsA(fc_first_arg, Var) &&
		((Var *) fc_first_arg)->varattno == InvalidAttrNumber)
	{
		ParseNamespaceItem *fc_nsitem;

		fc_nsitem = GetNSItemByRangeTablePosn(fc_pstate,
										   ((Var *) fc_first_arg)->varno,
										   ((Var *) fc_first_arg)->varlevelsup);
		/* 如果 funcname 匹配列，则返回 Var，否则返回 NULL */
		return scanNSItemForColumn(fc_pstate, fc_nsitem,
								   ((Var *) fc_first_arg)->varlevelsup,
								   fc_funcname, fc_location);
	}

	
	if (IsA(fc_first_arg, Var) &&
		((Var *) fc_first_arg)->vartype == RECORDOID)
		fc_tupdesc = expandRecordVariable(fc_pstate, (Var *) fc_first_arg, 0);
	else
		fc_tupdesc = get_expr_result_tupdesc(fc_first_arg, true);
	if (!fc_tupdesc)
		return NULL;			/* 无法解析的 RECORD 类型 */

	for (fc_i = 0; fc_i < fc_tupdesc->natts; fc_i++)
	{
		Form_pg_attribute fc_att = TupleDescAttr(fc_tupdesc, fc_i);

		if (strcmp(fc_funcname, NameStr(fc_att->attname)) == 0 &&
			!fc_att->attisdropped)
		{
			/* 成功，因此生成 FieldSelect 表达式 */
			FieldSelect *fc_fselect = makeNode(FieldSelect);

			fc_fselect->arg = (Expr *) fc_first_arg;
			fc_fselect->fieldnum = fc_i + 1;
			fc_fselect->resulttype = fc_att->atttypid;
			fc_fselect->resulttypmod = fc_att->atttypmod;
			/* 保存属性的排序规则以供 parse_collate.c 使用 */
			fc_fselect->resultcollid = fc_att->attcollation;
			return (Node *) fc_fselect;
		}
	}

	return NULL;				/* funcname 与任何列不匹配 */
}

/*
 * funcname_signature_string
 *		构建表示函数名称的字符串，包括参数类型。
 *		结果类似于 "foo(integer)"。
 *
 * 如果 argnames 不是 NIL，它是一个表示最后 N 个参数实际
 * 参数名称的 C 字符串列表。这在处理命名调用函数时
 * 也必须视为函数签名的一部分。
 *
 * 通常用于构造函数未找到错误的信息。
 */
const char * funcname_signature_string(const char *fc_funcname, int fc_nargs,
						  List *fc_argnames, const Oid *fc_argtypes)
{
	StringInfoData fc_argbuf;
	int			fc_numposargs;
	ListCell   *fc_lc;
	int			fc_i;

	initStringInfo(&fc_argbuf);

	appendStringInfo(&fc_argbuf, "%s(", fc_funcname);

	fc_numposargs = fc_nargs - list_length(fc_argnames);
	fc_lc = list_head(fc_argnames);

	for (fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		if (fc_i)
			appendStringInfoString(&fc_argbuf, ", ");
		if (fc_i >= fc_numposargs)
		{
			appendStringInfo(&fc_argbuf, "%s => ", (char *) lfirst(fc_lc));
			fc_lc = lnext(fc_argnames, fc_lc);
		}
		appendStringInfoString(&fc_argbuf, format_type_be(fc_argtypes[fc_i]));
	}

	appendStringInfoChar(&fc_argbuf, ')');

	return fc_argbuf.data;			/* 返回 palloc 的字符串缓冲区 */
}

/*
 * func_signature_string
 *		与上面相同，但函数名称作为合格名称列表传递。
 */
const char * func_signature_string(List *fc_funcname, int fc_nargs,
					  List *fc_argnames, const Oid *fc_argtypes)
{
	return funcname_signature_string(NameListToString(fc_funcname),
									 fc_nargs, fc_argnames, fc_argtypes);
}

/*
 * LookupFuncNameInternal
 *		LookupFuncName/LookupFuncWithArgs 的核心
 *
 * 在错误情况下，例如找不到函数，我们返回
 * InvalidOid 并设置 *lookupError 以指示发生了什么错误。
 *
 * 可能的错误：
 *	FUNCLOOKUP_NOSUCHFUNC: 我们找不到这个名称的函数。
 *	FUNCLOOKUP_AMBIGUOUS: 超过一个函数匹配。
 */
static Oid fc_LookupFuncNameInternal(ObjectType fc_objtype, List *fc_funcname,
					   int fc_nargs, const Oid *fc_argtypes,
					   bool fc_include_out_arguments, bool fc_missing_ok,
					   FuncLookupError *fc_lookupError)
{
	Oid			fc_result = InvalidOid;
	FuncCandidateList fc_clist;

	/* 仅允许对无参数函数使用 NULL argtypes */
	Assert(fc_argtypes != NULL || fc_nargs == 0);

	/* 始终设置 *lookupError，以避免未初始化变量的警告 */
	*fc_lookupError = FUNCLOOKUP_NOSUCHFUNC;

	/* 获取候选对象列表 */
	fc_clist = FuncnameGetCandidates(fc_funcname, fc_nargs, NIL, false, false,
								  fc_include_out_arguments, fc_missing_ok);

	/* 扫描列表以匹配参数类型（如果指定）和 objtype */
	for (; fc_clist != NULL; fc_clist = fc_clist->next)
	{
		/* 检查参数类型是否匹配，如果指定 */
		if (fc_nargs >= 0)
		{
			/* 如果 nargs==0，argtypes 可以为 null；不要将其传递给 memcmp */
			if (fc_nargs > 0 &&
				memcmp(fc_argtypes, fc_clist->args, fc_nargs * sizeof(Oid)) != 0)
				continue;
		}

		/* 检查 FuncnameGetCandidates 报告的重复项 */
		if (!OidIsValid(fc_clist->oid))
		{
			*fc_lookupError = FUNCLOOKUP_AMBIGUOUS;
			return InvalidOid;
		}

		/* 检查 objtype 是否匹配，如果指定 */
		switch (fc_objtype)
		{
			case OBJECT_FUNCTION:
			case OBJECT_AGGREGATE:
				/* 忽略过程 */
				if (get_func_prokind(fc_clist->oid) == PROKIND_PROCEDURE)
					continue;
				break;
			case OBJECT_PROCEDURE:
				/* 忽略非过程 */
				if (get_func_prokind(fc_clist->oid) != PROKIND_PROCEDURE)
					continue;
				break;
			case OBJECT_ROUTINE:
				/* 无限制 */
				break;
			default:
				Assert(false);
		}

		/* 检查多个匹配项 */
		if (OidIsValid(fc_result))
		{
			*fc_lookupError = FUNCLOOKUP_AMBIGUOUS;
			return InvalidOid;
		}

		/* 好的，我们有一个候选项 */
		fc_result = fc_clist->oid;
	}

	return fc_result;
}

/*
 * LookupFuncName
 *
 * 给定一个可能合格的函数名称和可选的参数类型集，查找该函数。
 * 传递 nargs == -1 以指示参数的数量和类型未指定（这与
 * 指定没有参数是不相同的）。
 *
 * 如果函数名称未标明模式，将在当前
 * 命名空间搜索路径中查找。
 *
 * 如果找不到函数，如果 missing_ok 为 true，则返回 InvalidOid，
 * 否则引发错误。
 *
 * 如果 nargs == -1 并且找到多个函数与此函数名称匹配，
 * 我们将引发模棱两可的函数错误，无论 missing_ok 设置为何值。
 *
 * 仅会找到函数；即使过程匹配名称和参数类型，也将被忽略。
 * （但是，我们不会烦恼地拒绝聚合函数或窗口函数。）
 */
Oid LookupFuncName(List *fc_funcname, int fc_nargs, const Oid *fc_argtypes, bool fc_missing_ok)
{
	Oid			fc_funcoid;
	FuncLookupError fc_lookupError;

	fc_funcoid = fc_LookupFuncNameInternal(OBJECT_FUNCTION,
									 fc_funcname, fc_nargs, fc_argtypes,
									 false, fc_missing_ok,
									 &fc_lookupError);

	if (OidIsValid(fc_funcoid))
		return fc_funcoid;

	switch (fc_lookupError)
	{
		case FUNCLOOKUP_NOSUCHFUNC:
			/* 让调用者处理当 missing_ok 为 true 时 */
			if (fc_missing_ok)
				return InvalidOid;

			if (fc_nargs < 0)
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_FUNCTION),
						 errmsg("could not find a function named \"%s\"",
								NameListToString(fc_funcname))));
			else
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_FUNCTION),
						 errmsg("function %s does not exist",
								func_signature_string(fc_funcname, fc_nargs,
													  NIL, fc_argtypes))));
			break;

		case FUNCLOOKUP_AMBIGUOUS:
			/* 无论 missing_ok 如何都引发错误 */
			ereport(ERROR,
					(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
					 errmsg("function name \"%s\" is not unique",
							NameListToString(fc_funcname)),
					 errhint("Specify the argument list to select the function unambiguously.")));
			break;
	}

	return InvalidOid;			/* 让编译器安静 */
}

/*
 * LookupFuncWithArgs
 *
 * 像 LookupFuncName，但参数类型由
 * ObjectWithArgs 节点指定。此外，该函数可以根据 objtype 参数检查结果是函数、过程还是聚合。
 * 传递 OBJECT_ROUTINE 以接受其中任何一个。
 *
 * 出于历史原因，我们在查找函数时也接受聚合。
 *
 * 当 missing_ok 为 true 时，我们不会对缺失的对象生成任何错误并返回 InvalidOid。
 * 其他类型的错误仍然可以被引发，无论 missing_ok 的值为何。
 */
Oid LookupFuncWithArgs(ObjectType fc_objtype, ObjectWithArgs *fc_func, bool fc_missing_ok)
{
	Oid			fc_argoids[FUNC_MAX_ARGS];
	int			fc_argcount;
	int			fc_nargs;
	int			fc_i;
	ListCell   *fc_args_item;
	Oid			fc_oid;
	FuncLookupError fc_lookupError;

	Assert(fc_objtype == OBJECT_AGGREGATE ||
		   fc_objtype == OBJECT_FUNCTION ||
		   fc_objtype == OBJECT_PROCEDURE ||
		   fc_objtype == OBJECT_ROUTINE);

	fc_argcount = list_length(fc_func->objargs);
	if (fc_argcount > FUNC_MAX_ARGS)
	{
		if (fc_objtype == OBJECT_PROCEDURE)
			ereport(ERROR,
					(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
					 errmsg_plural("procedures cannot have more than %d argument",
								   "procedures cannot have more than %d arguments",
								   FUNC_MAX_ARGS,
								   FUNC_MAX_ARGS)));
		else
			ereport(ERROR,
					(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
					 errmsg_plural("functions cannot have more than %d argument",
								   "functions cannot have more than %d arguments",
								   FUNC_MAX_ARGS,
								   FUNC_MAX_ARGS)));
	}

	/*
	 * 首先，执行查找，仅考虑输入参数（传统的
	 * Postgres 规则）。
	 */
	fc_i = 0;
	foreach(fc_args_item, fc_func->objargs)
	{
		TypeName   *fc_t = lfirst_node(TypeName, fc_args_item);

		fc_argoids[fc_i] = LookupTypeNameOid(NULL, fc_t, fc_missing_ok);
		if (!OidIsValid(fc_argoids[fc_i]))
			return InvalidOid;	/* missing_ok 必须为 true */
		fc_i++;
	}

	/*
	 * 设置 nargs 以供 LookupFuncNameInternal 使用。它期望 -1 表示未指定任何参数。
	 */
	fc_nargs = fc_func->args_unspecified ? -1 : fc_argcount;

	/*
	 * 在 args_unspecified 模式下，还告诉 LookupFuncNameInternal 考虑
	 * 对象类型，因为没有理由不这样做。然而，如果我们
	 * 有参数列表，则禁用 objtype 检查，因为我们宁愿抱怨“对象类型错误”
	 * 而不是“对象不存在”。（请注意，有参数时，FuncnameGetCandidates
	 * 将确保只有一个参数类型匹配，因此通过这个选择不会冒险出现歧义故障。）
	 */
	fc_oid = fc_LookupFuncNameInternal(fc_func->args_unspecified ? fc_objtype : OBJECT_ROUTINE,
								 fc_func->objname, fc_nargs, fc_argoids,
								 false, fc_missing_ok,
								 &fc_lookupError);

	/*
	 * 如果指定了 PROCEDURE 或 ROUTINE，并且我们有一个参数列表
	 * 并且不包含参数模式标记，并且我们尚未发现
	 * 有歧义，则执行查找考虑所有参数。
	 * （注意：对于零参数过程或在 args_unspecified 模式下，
	 * 正常查找就足够了；因此要求非 NIL objfuncargs 执行此查找是可以的。）
	 */
	if ((fc_objtype == OBJECT_PROCEDURE || fc_objtype == OBJECT_ROUTINE) &&
		fc_func->objfuncargs != NIL &&
		fc_lookupError != FUNCLOOKUP_AMBIGUOUS)
	{
		bool		fc_have_param_mode = false;

		/*
		 * 检查非默认参数模式标记。如果有任何标记，
		 * 则该命令不符合 SQL 规格语法，因此我们可以
		 * 假设仅考虑输入参数的传统 Postgres 查找方法就足够了。 
		 * （请注意，由于规格对函数没有 OUT 参数，
		 * 因此在 FUNCTION 或 AGGREGATE 模式下我们也不需要这个
		 * 黑客手法。）
		 */
		foreach(fc_args_item, fc_func->objfuncargs)
		{
			FunctionParameter *fc_fp = lfirst_node(FunctionParameter, fc_args_item);

			if (fc_fp->mode != FUNC_PARAM_DEFAULT)
			{
				fc_have_param_mode = true;
				break;
			}
		}

		if (!fc_have_param_mode)
		{
			Oid			fc_poid;

			/* 没有模式标记，objargs 一定包含所有参数 */
			Assert(list_length(fc_func->objfuncargs) == fc_argcount);

			/* 对于 objtype == OBJECT_PROCEDURE，我们可以忽略非过程 */
			fc_poid = fc_LookupFuncNameInternal(fc_objtype, fc_func->objname,
										  fc_argcount, fc_argoids,
										  true, fc_missing_ok,
										  &fc_lookupError);

			/* 合并结果，处理歧义 */
			if (OidIsValid(fc_poid))
			{
				if (OidIsValid(fc_oid) && fc_oid != fc_poid)
				{
					/* 哎呀，我们在不同对象上都得到了结果 */
					fc_oid = InvalidOid;
					fc_lookupError = FUNCLOOKUP_AMBIGUOUS;
				}
				else
					fc_oid = fc_poid;
			}
			else if (fc_lookupError == FUNCLOOKUP_AMBIGUOUS)
				fc_oid = InvalidOid;
		}
	}

	if (OidIsValid(fc_oid))
	{
		/*
		 * 即使找到函数，也要验证 objtype
		 * 是否与找到的函数的 prokind 匹配。出于历史原因，
		 * 我们允许 FUNCTION 的 objtype 包括聚合和窗口
		 * 函数；但如果对象是过程，我们就不这样做。 
		 * 这是一个足够新的特性，因此这个历史规则不适用。
		 *
		 * （这个检查在某种程度上与 LookupFuncNameInternal 中的 objtype 检查是部分冗余的；
		 * 但并不完全，因为我们通常不会告诉
		 * LookupFuncNameInternal 完全应用该检查。）
		 */
		switch (fc_objtype)
		{
			case OBJECT_FUNCTION:
				/* 只有在是过程时才会抱怨。 */
				if (get_func_prokind(fc_oid) == PROKIND_PROCEDURE)
					ereport(ERROR,
							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
							 errmsg("%s is not a function",
									func_signature_string(fc_func->objname, fc_argcount,
														  NIL, fc_argoids))));
				break;

			case OBJECT_PROCEDURE:
				/* 如果找到的对象不是过程，则拒绝。 */
				if (get_func_prokind(fc_oid) != PROKIND_PROCEDURE)
					ereport(ERROR,
							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
							 errmsg("%s is not a procedure",
									func_signature_string(fc_func->objname, fc_argcount,
														  NIL, fc_argoids))));
				break;

			case OBJECT_AGGREGATE:
				/* 如果找到的对象不是聚合，则拒绝。 */
				if (get_func_prokind(fc_oid) != PROKIND_AGGREGATE)
					ereport(ERROR,
							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
							 errmsg("function %s is not an aggregate",
									func_signature_string(fc_func->objname, fc_argcount,
														  NIL, fc_argoids))));
				break;

			default:
				/* OBJECT_ROUTINE 接受任何东西。 */
				break;
		}

		return fc_oid;				/* All good */
	}
	else
	{
		/* 处理查找失败的情况 */
		switch (fc_lookupError)
		{
			case FUNCLOOKUP_NOSUCHFUNC:
				/* 当 missing_ok 为 true 时抑制“没有这样的函数”错误 */
				if (fc_missing_ok)
					break;

				switch (fc_objtype)
				{
					case OBJECT_PROCEDURE:
						if (fc_func->args_unspecified)
							ereport(ERROR,
									(errcode(ERRCODE_UNDEFINED_FUNCTION),
									 errmsg("could not find a procedure named \"%s\"",
											NameListToString(fc_func->objname))));
						else
							ereport(ERROR,
									(errcode(ERRCODE_UNDEFINED_FUNCTION),
									 errmsg("procedure %s does not exist",
											func_signature_string(fc_func->objname, fc_argcount,
																  NIL, fc_argoids))));
						break;

					case OBJECT_AGGREGATE:
						if (fc_func->args_unspecified)
							ereport(ERROR,
									(errcode(ERRCODE_UNDEFINED_FUNCTION),
									 errmsg("could not find an aggregate named \"%s\"",
											NameListToString(fc_func->objname))));
						else if (fc_argcount == 0)
							ereport(ERROR,
									(errcode(ERRCODE_UNDEFINED_FUNCTION),
									 errmsg("aggregate %s(*) does not exist",
											NameListToString(fc_func->objname))));
						else
							ereport(ERROR,
									(errcode(ERRCODE_UNDEFINED_FUNCTION),
									 errmsg("aggregate %s does not exist",
											func_signature_string(fc_func->objname, fc_argcount,
																  NIL, fc_argoids))));
						break;

					default:
						/* FUNCTION 和 ROUTINE */
						if (fc_func->args_unspecified)
							ereport(ERROR,
									(errcode(ERRCODE_UNDEFINED_FUNCTION),
									 errmsg("could not find a function named \"%s\"",
											NameListToString(fc_func->objname))));
						else
							ereport(ERROR,
									(errcode(ERRCODE_UNDEFINED_FUNCTION),
									 errmsg("function %s does not exist",
											func_signature_string(fc_func->objname, fc_argcount,
																  NIL, fc_argoids))));
						break;
				}
				break;

			case FUNCLOOKUP_AMBIGUOUS:
				switch (fc_objtype)
				{
					case OBJECT_FUNCTION:
						ereport(ERROR,
								(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
								 errmsg("function name \"%s\" is not unique",
										NameListToString(fc_func->objname)),
								 fc_func->args_unspecified ?
								 errhint("Specify the argument list to select the function unambiguously.") : 0));
						break;
					case OBJECT_PROCEDURE:
						ereport(ERROR,
								(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
								 errmsg("procedure name \"%s\" is not unique",
										NameListToString(fc_func->objname)),
								 fc_func->args_unspecified ?
								 errhint("Specify the argument list to select the procedure unambiguously.") : 0));
						break;
					case OBJECT_AGGREGATE:
						ereport(ERROR,
								(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
								 errmsg("aggregate name \"%s\" is not unique",
										NameListToString(fc_func->objname)),
								 fc_func->args_unspecified ?
								 errhint("Specify the argument list to select the aggregate unambiguously.") : 0));
						break;
					case OBJECT_ROUTINE:
						ereport(ERROR,
								(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
								 errmsg("routine name \"%s\" is not unique",
										NameListToString(fc_func->objname)),
								 fc_func->args_unspecified ?
								 errhint("Specify the argument list to select the routine unambiguously.") : 0));
						break;

					default:
						Assert(false);	/* 由上面的 Assert 禁止 */
						break;
				}
				break;
		}

		return InvalidOid;
	}
}

/*
 * check_srf_call_placement
 *		验证 set-returning 函数是否在有效的位置被调用，
 *		如果不符合则抛出一个友好的错误。
 *
 * 侧面效果是在适当时将 pstate->p_hasTargetSRFs 设为 true。
 *
 * last_srf 应该是 pstate->p_last_srf 的副本，
 * 该副本是在我们开始转换函数参数之前。
 * 这允许检测 SRF 的参数是否包含任何 SRFs。
 */
void check_srf_call_placement(ParseState *fc_pstate, Node *fc_last_srf, int fc_location)
{
	const char *fc_err;
	bool		fc_errkind;

	/*
	 * 检查 set-returning 函数是否在查询中的无效位置。
	 * 基本上，我们不允许 SRFs 出现在目标列表中（包括 GROUP BY/ORDER BY 表达式）、VALUES 和 FROM 中的函数。
	 *
	 * 为了简洁，我们在此支持两种报告错误的方案：将 "err" 设置为自定义消息，或如果错误上下文
	 * 由 ParseExprKindName 返回的内容能够充分识别，则将 "errkind" 设置为 true，*并且*
	 * 返回的只是一个 SQL 关键字。
	 * （否则，使用自定义消息以避免创建翻译问题。）
	 */
	fc_err = NULL;
	fc_errkind = false;
	switch (fc_pstate->p_expr_kind)
	{
		case EXPR_KIND_NONE:
			Assert(false);		/* 不能发生 */
			break;
		case EXPR_KIND_OTHER:
			/* 在这里接受 SRF；调用者必须在需要时抛出错误 */
			break;
		case EXPR_KIND_JOIN_ON:
		case EXPR_KIND_JOIN_USING:
			fc_err = _("set-returning functions are not allowed in JOIN conditions");
			break;
		case EXPR_KIND_FROM_SUBSELECT:
			/* 不能到这里，但以防万一，抛出错误 */
			fc_errkind = true;
			break;
		case EXPR_KIND_FROM_FUNCTION:
			/* 可以，但我们不允许在此处嵌套 SRFs */
			/* errmsg 选择与 transformRangeFunction() 匹配 */
			/* errposition 应指向内部 SRF */
			if (fc_pstate->p_last_srf != fc_last_srf)
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("set-returning functions must appear at top level of FROM"),
						 parser_errposition(fc_pstate,
											exprLocation(fc_pstate->p_last_srf))));
			break;
		case EXPR_KIND_WHERE:
			fc_errkind = true;
			break;
		case EXPR_KIND_POLICY:
			fc_err = _("set-returning functions are not allowed in policy expressions");
			break;
		case EXPR_KIND_HAVING:
			fc_errkind = true;
			break;
		case EXPR_KIND_FILTER:
			fc_errkind = true;
			break;
		case EXPR_KIND_WINDOW_PARTITION:
		case EXPR_KIND_WINDOW_ORDER:
			/* 可以，这实际上是 GROUP BY/ORDER BY */
			fc_pstate->p_hasTargetSRFs = true;
			break;
		case EXPR_KIND_WINDOW_FRAME_RANGE:
		case EXPR_KIND_WINDOW_FRAME_ROWS:
		case EXPR_KIND_WINDOW_FRAME_GROUPS:
			fc_err = _("set-returning functions are not allowed in window definitions");
			break;
		case EXPR_KIND_SELECT_TARGET:
		case EXPR_KIND_INSERT_TARGET:
			/* 可以 */
			fc_pstate->p_hasTargetSRFs = true;
			break;
		case EXPR_KIND_UPDATE_SOURCE:
		case EXPR_KIND_UPDATE_TARGET:
			/* 不允许，因为这样会造成歧义 */
			fc_errkind = true;
			break;
		case EXPR_KIND_GROUP_BY:
		case EXPR_KIND_ORDER_BY:
			/* 可以 */
			fc_pstate->p_hasTargetSRFs = true;
			break;
		case EXPR_KIND_DISTINCT_ON:
			/* 可以 */
			fc_pstate->p_hasTargetSRFs = true;
			break;
		case EXPR_KIND_LIMIT:
		case EXPR_KIND_OFFSET:
			fc_errkind = true;
			break;
		case EXPR_KIND_RETURNING:
			fc_errkind = true;
			break;
		case EXPR_KIND_VALUES:
			/* SRFs 目前不被 nodeValuesscan.c 支持 */
			fc_errkind = true;
			break;
		case EXPR_KIND_VALUES_SINGLE:
			/* 可以，因为我们像处理 SELECT tlist 一样处理这个 */
			fc_pstate->p_hasTargetSRFs = true;
			break;
		case EXPR_KIND_MERGE_WHEN:
			fc_err = _("set-returning functions are not allowed in MERGE WHEN conditions");
			break;
		case EXPR_KIND_CHECK_CONSTRAINT:
		case EXPR_KIND_DOMAIN_CHECK:
			fc_err = _("set-returning functions are not allowed in check constraints");
			break;
		case EXPR_KIND_COLUMN_DEFAULT:
		case EXPR_KIND_FUNCTION_DEFAULT:
			fc_err = _("set-returning functions are not allowed in DEFAULT expressions");
			break;
		case EXPR_KIND_INDEX_EXPRESSION:
			fc_err = _("set-returning functions are not allowed in index expressions");
			break;
		case EXPR_KIND_INDEX_PREDICATE:
			fc_err = _("set-returning functions are not allowed in index predicates");
			break;
		case EXPR_KIND_STATS_EXPRESSION:
			fc_err = _("set-returning functions are not allowed in statistics expressions");
			break;
		case EXPR_KIND_ALTER_COL_TRANSFORM:
			fc_err = _("set-returning functions are not allowed in transform expressions");
			break;
		case EXPR_KIND_EXECUTE_PARAMETER:
			fc_err = _("set-returning functions are not allowed in EXECUTE parameters");
			break;
		case EXPR_KIND_TRIGGER_WHEN:
			fc_err = _("set-returning functions are not allowed in trigger WHEN conditions");
			break;
		case EXPR_KIND_PARTITION_BOUND:
			fc_err = _("set-returning functions are not allowed in partition bound");
			break;
		case EXPR_KIND_PARTITION_EXPRESSION:
			fc_err = _("set-returning functions are not allowed in partition key expressions");
			break;
		case EXPR_KIND_CALL_ARGUMENT:
			fc_err = _("set-returning functions are not allowed in CALL arguments");
			break;
		case EXPR_KIND_COPY_WHERE:
			fc_err = _("set-returning functions are not allowed in COPY FROM WHERE conditions");
			break;
		case EXPR_KIND_GENERATED_COLUMN:
			fc_err = _("set-returning functions are not allowed in column generation expressions");
			break;
		case EXPR_KIND_CYCLE_MARK:
			fc_errkind = true;
			break;

			/*
			 * 这里故意没有默认情况，以便编译器会警告如果我们添加一个新的 ParseExprKind 而没有扩展这个开关。如果我们在运行时看到一个未被识别的值，行为将与 EXPR_KIND_OTHER 相同，这在任何情况下都是合理的。
			 */
	}
	if (fc_err)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg_internal("%s", fc_err),
				 parser_errposition(fc_pstate, fc_location)));
	if (fc_errkind)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
		/* 翻译者：%s 是 SQL 构造的名称，例如 GROUP BY */
				 errmsg("set-returning functions are not allowed in %s",
						ParseExprKindName(fc_pstate->p_expr_kind)),
				 parser_errposition(fc_pstate, fc_location)));
}
